const fs = require('fs');//node的文件系统模块，用于读写及操作文件
const path = require('path');//node提供的一些用于处理文件路径的小工具
var chalk = require('chalk')//用于向控制台输出带颜色的问题提示
// 2、模块对外暴露的 js 函数
function JsEncodePlugin(pluginOptions) {
  this.options = pluginOptions;
}
// 3、原型定义一个 apply 函数，并注入了 compiler 对象
JsEncodePlugin.prototype.apply = function (compiler) {
    const _this = this;
    // 4、挂载 webpack 事件钩子（这里挂载的是 after-emit 事件,在将内存中 assets 内容写到磁盘文件夹之后触发的webpack生命周期钩子）
    compiler.plugin('after-emit', function (compilation, callback) {
        // ... 内部进行自定义的编译操作
        // 5、操作 compilation 对象的内部数据
        console.log(chalk.cyan('\n jsencode start.\n'))
        var filePath = path.resolve(__dirname, _this.options.assetsPath);//设置需要加密的js文件路径，_this.options.assetsPath为插件配置中传过来的需要加密的js文件路径
        filterFile(filePath);
        function filterFile(fp){
          fs.readdir(fp, (err, files)=>{//读取该文件路径
            if(err){
              console.log(chalk.yellow(
                '读取js文件夹异常：\n' +
                err.message + '\n'
              ))
              return;
            }
            files.forEach((filename)=>{//遍历该路径下所有文件
              if(_this.options.jsReg.test(filename)){//利用正则匹配我们要加密的文件,_this.options.jsReg为插件中传过来的需要加密的js文件正则，用以筛选出我们需要加密的js文件。
                var filedir = path.resolve(fp, filename);
                fs.readFile(filedir, 'utf-8', (err, data)=>{//读取文件源码
                  if(err){
                    console.log(chalk.yellow(
                      '读取js文件异常：\n' +
                      err.message + '\n'
                    ))
                    return;
                  }
          //调用jjencode函数对源码进行jjencode加密，_this.options.global为插件配置中传过来的加密使用的全局变量名，将在jjencode函数中作为第一个参数传入
                  let result = jjencode(_this.options.global, data);
                  fs.writeFile(filedir, result, (err)=>{//将加密后的代码写回文件中
                    if(err){
                      console.log(chalk.yellow(
                        '写入加密后的js文件异常：\n' +
                        err.message + '\n'
                      ))
                      return;
                    }
                    console.log(chalk.cyan('  jsencode complete.\n'))
                  })
                })
              }
            })
          })
        }
        //js加密函数
        function jjencode( gv, text )
              {
                  var r="";
                  var n;
                  var t;
                  var b=[ "___", "__$", "_$_", "_$$", "$__", "$_$", "$$_", "$$$", "$___", "$__$", "$_$_", "$_$$", "$$__", "$$_$", "$$$_", "$$$$", ];
                  var s = "";
                  for( var i = 0; i < text.length; i++ ){
                      n = text.charCodeAt( i );
                      if( n == 0x22 || n == 0x5c ){
                          s += "\\\\\\" + text.charAt( i ).toString(16);
                      }else if( (0x20 <= n && n <= 0x2f) || (0x3A <= n == 0x40) || ( 0x5b <= n && n <= 0x60 ) || ( 0x7b <= n && n <= 0x7f ) ){
                          s += text.charAt( i );
                      }else if( (0x30 <= n && n <= 0x39 ) || (0x61 <= n && n <= 0x66 ) ){
                          if( s ) r += "\"" + s +"\"+";
                          r += gv + "." + b[ n < 0x40 ? n - 0x30 : n - 0x57 ] + "+";
                          s="";
                      }else if( n == 0x6c ){ // 'l'
                          if( s ) r += "\"" + s + "\"+";
                          r += "(![]+\"\")[" + gv + "._$_]+";
                          s = "";
                      }else if( n == 0x6f ){ // 'o'
                          if( s ) r += "\"" + s + "\"+";
                          r += gv + "._$+";
                          s = "";
                      }else if( n == 0x74 ){ // 'u'
                          if( s ) r += "\"" + s + "\"+";
                          r += gv + ".__+";
                          s = "";
                      }else if( n == 0x75 ){ // 'u'
                          if( s ) r += "\"" + s + "\"+";
                          r += gv + "._+";
                          s = "";
                      }else if( n < 128 ){
                          if( s ) r += "\"" + s;
                          else r += "\"";
                          r += "\\\\\"+" + n.toString( 8 ).replace( /[0-7]/g, function(c){ return gv + "."+b[ c ]+"+" } );
                          s = "";
                      }else{
                          if( s ) r += "\"" + s;
                          else r += "\"";
                          r += "\\\\\"+" + gv + "._+" + n.toString(16).replace( /[0-9a-f]/gi, function(c){ return gv + "."+b[parseInt(c,16)]+"+"} );
                          s = "";
                      }
                  }
                  if( s ) r += "\"" + s + "\"+";
   
                  r = 
                  gv + "=~[];" + 
                  gv + "={___:++" + gv +",$$$$:(![]+\"\")["+gv+"],__$:++"+gv+",$_$_:(![]+\"\")["+gv+"],_$_:++"+
                  gv+",$_$$:({}+\"\")["+gv+"],$$_$:("+gv+"["+gv+"]+\"\")["+gv+"],_$$:++"+gv+",$$$_:(!\"\"+\"\")["+
                  gv+"],$__:++"+gv+",$_$:++"+gv+",$$__:({}+\"\")["+gv+"],$$_:++"+gv+",$$$:++"+gv+",$___:++"+gv+",$__$:++"+gv+"};"+
                  gv+".$_="+
                  "("+gv+".$_="+gv+"+\"\")["+gv+".$_$]+"+
                  "("+gv+"._$="+gv+".$_["+gv+".__$])+"+
                  "("+gv+".$$=("+gv+".$+\"\")["+gv+".__$])+"+
                  "((!"+gv+")+\"\")["+gv+"._$$]+"+
                  "("+gv+".__="+gv+".$_["+gv+".$$_])+"+
                  "("+gv+".$=(!\"\"+\"\")["+gv+".__$])+"+
                  "("+gv+"._=(!\"\"+\"\")["+gv+"._$_])+"+
                  gv+".$_["+gv+".$_$]+"+
                  gv+".__+"+
                  gv+"._$+"+
                  gv+".$;"+
                  gv+".$$="+
                  gv+".$+"+
                  "(!\"\"+\"\")["+gv+"._$$]+"+
                  gv+".__+"+
                  gv+"._+"+
                  gv+".$+"+
                  gv+".$$;"+
                  gv+".$=("+gv+".___)["+gv+".$_]["+gv+".$_];"+
                  gv+".$("+gv+".$("+gv+".$$+\"\\\"\"+" + r + "\"\\\"\")())();";
                  //console.log(r);
                  return r;
              }
        // 6、执行 callback 回调
        callback();
    });
  };
// 暴露 js 函数
module.exports =JsEncodePlugin;